List 集合遍历过程中删除元素。这个坑踩一遍就不要再踩了! |
您所在的位置:网站首页 › java list清空数据 › List 集合遍历过程中删除元素。这个坑踩一遍就不要再踩了! |
作为一名后端开发,不管采用什么语言 使用 List 集合的频率都非常高。 对 List 集合的遍历和遍历中操作数据也是家常便饭。 我从我使用的过程中对于此问题的思考与实践形成记录,与大家交流。有不对的地方,恳请大家指正。 1. 遍历List 集合的遍历有很多种方式,每一种遍历方式只有性能上的差异,不会有异常和暗坑。这里不再赘述。 2. 遍历过程中删除元素因为 List 集合有多种遍历方式,也就意味着存在多种遍历中删除的方式: 在做各种方式比对前,我们先加入一些初始数据。 List list = new ArrayList(); list.add("snow"); list.add("nier"); list.add("sar"); list.add("juaya"); list.add("rock"); list.add("snow"); list.add("nier"); 2.1 for 简单循环正向遍历方式 for (int i = 0; i list.remove(i); } if(name.equals("nier")){ list.remove(i); } } System.out.println(list);上述代码打印结果: [nier, sar, juaya, rock, nier]很明显 这不是正确的结果。 这是 因为:在遍历过程中,删除或者增加元素后,集合长度会因为实时改动而改动,也就是说集合在遍历过程中忘了初心了。 举个栗子:现在对长度为 5 的集合进行遍历,在遍历到 第三个元素的时候删除了ta,那么此时集合长度成了4,那么此时集合就不会遍历到之前下标为 5 的元素了。 知道了原因后,我们可以对症下药,达到我们的预期。修改代码如下: for (int i = 0; i list.remove(i); // 加补偿机制 i--; } if(name.equals("nier")){ list.remove(i); // 加补偿机制 i--; } } System.out.println(list);上述代码执行结果: [sar, juaya, rock]如此 加入了补偿机制后虽然达到了预期结果。但是很明显代码不够优雅。So 这种方式不推荐。 2.2 for 简单循环反向遍历方式反向遍历和正向遍历类似,只不过是反方向的钟而已。 for (int i = list.size() - 1; i >= 0; i--) { String name = list.get(i); if(name.equals("snow")){ list.remove(i); } if(name.equals("nier")){ list.remove(i); } } System.out.println(list);上述代码执行结果: [sar, juaya, rock]咦~可以哎。结果正确。那这种方式是不是就绝对可以了呢。 NO! 逆序遍历虽然在这个场景下达到了预期的正确结果,但是因为 这种方式和正序在原理上是相同的。所以在遍历过程中操作元素也会有其劣根性。比如在逆序遍历过程中加入元素,依然会对整个遍历产生影响。 所以这也不是推荐的方式。 2.3 foreach 方式遍历删除单从遍历来看 这种方式比上述两种方式优雅了不少。但是遍历过程中操作元素能达到预期结果吗? for (String s : list) { if(s.equals("rock")){ list.remove("rock"); } }执行结果… What??? 可以看出:调用next()方法获取下一个元素时,第一行代码就是调用了checkForComodification(); 而该方法的核心逻辑就是比较 modCount 和 expectedModCount 这2个变量的值。 在上面的例子中,刚开始 modCount 和 expectedModCount 的值都为 n,所以第1次获取元素 “rock” 是没问题的,但是当执行完下面这行代码时: list.remove("rock");modCount 的值就被修改了。 执行结果: [nier, sar, juaya, rock, nier]很 nice ! 那这种方式怎么就可以删除了呢??? 因为: 这种方式每次删除一个元素,都会将modCount 的值重新赋值给 expectedModCount,这样2个变量就相等了,不会触发 java.util.ConcurrentModificationException 异常。 虽然这种方式符合了业务要求,但是还是不推荐这种方式,因为他的写法依然不够优雅。 2.5 removeIf() (推荐)从JDK1.8开始,可以使用 removeIf() 方法来代替 Iterator的remove()方法实现一边遍历一边删除。 list.removeIf(s -> s.equals("snow")); System.out.println(list);打印结果: [nier, sar, juaya, rock, nier]其源码如下: filter 过滤会保留满足条件的。 结果如下: [nier, sar, juaya, rock, nier] |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |